/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 *
 * @author jefoutta
 */
import java.util.*;

public abstract class LogicGate {
    protected int numEntries;
    protected boolean[] inputAvailable;
    protected int[] input;
    protected int[] delayUp;
    protected int[] delayDown;
    protected int output;
    protected ArrayList<Connection> outputChannels;
    protected ArrayList<Connection> inputChannels;
    protected int type;
    protected String name;
    protected int pathCount;
    protected boolean error;
    protected int errorCount;
    public static boolean bigError = false;
    public void setName(String tname){
        name = tname;
    }

    public void setType(int ttype){
        type = ttype;
    }

    public void setInput(int value,int index){
        input[index] = value;
    }

    public String getName(){
        return name;
    }

    public int getType(){
        return type;
    }
    
    public int getInput(int index){
        return input[index];
    }

    public int getAvailableInputIndex(){
        int i = 0;
        while(i < numEntries){
            if(this.inputAvailable[i] == true){
                this.inputAvailable[i] = false;
                break;
            }
            i++;
        }
        if(i == numEntries){
            return -2;
        }
        return i;
    }

    public void propagateIn(int index){
        if(this.bigError == false){
            if(this.inputChannels.get(index).getType() == 'g'){
                this.inputChannels.get(index).getSourceGate().propagateOut(this.input[index]);
                this.inputChannels.get(index).getSourceGate().justifyFromOutput(this.input[index]);
            }else
                if(this.inputChannels.get(index).getType() == 's'){
                    this.inputChannels.get(index).getSource().propagateOut(this.input[index]);
                }
        }else{
            System.out.println("Error en propagar " + this.name);
        }
        if(this.bigError == true){
            System.out.println("error al salir de propagateIn " + this.name);
        }
    }

    public void propagateOut(int tval){
        System.out.println("entro a propagate Out " +this.name + " val: "+ tval);
        if(this.bigError == false){
        for(int i = 0; i < this.outputChannels.size(); i++)
           if((this.outputChannels.get(i).getDestinyGate().getInput(this.outputChannels.get(i).getInputIndex()) == -1)||(this.outputChannels.get(i).getDestinyGate().getInput(this.outputChannels.get(i).getInputIndex()) == tval)){
               this.outputChannels.get(i).getDestinyGate().copyVal(tval, this.outputChannels.get(i).getInputIndex());
            }
            else{
                this.outputChannels.get(i).getDestinyGate().setError(true);
            }
        }else{
            System.out.println("Error en propagarOut" + this.name);
        }
        if(this.bigError == true){
            System.out.println("error al salir de propagateOut " + this.name);
        }
        System.out.println("salió a propagate Out " +this.name);
    }

    public void copyVal(int val, int tindex){
        this.input[tindex] = val;
        System.out.println("copyVal " + tindex +" "+this.name);
    }

    public void setOutputChannel(Connection connection){
        outputChannels.add(connection);
        pathCount++;
    }

    public void setInputChannel(Connection connection){
        inputChannels.add(connection);
    }

    public void reset(){
        this.error = false;
        this.errorCount = 0;
        for(int i = 0; i < input.length; i++)
            input[i] = -1;
        output = -1;
    }

    public void semicompleteReset(){
        this.error = false;
        this.errorCount = 0;
        for(int i = 0; i < input.length; i++)
            input[i] = -1;
        output = -1;
    }

    public void completeReset(){
        this.error = false;
        this.errorCount = 0;
        for(int i = 0; i < input.length; i++)
            input[i] = -1;
        output = -1;
        this.pathCount = this.outputChannels.size();
    }

    protected void setError(boolean value){
        this.error = value;
        this.errorCount++;
        this.bigError = true;
    }

    protected void setError(boolean value, String message){
        this.error = value;
        System.out.println(message);
        this.errorCount++;
    }

    protected void sensitize(int index){
        System.out.println("sensitize gate: " + this.name);
        if(this.bigError == false){
            this.sensitizeEntry(index);
            if(this.type != 3){
                if(index == 1){
                    this.propagateIn(0);
                }
                if(index == 0){
                    this.propagateIn(1);
                }
            }else{
            }
            this.output = 2;
            if(this.outputChannels.size() > 0){
                if(this.outputChannels.size() > 1){
                    System.out.println(this.pathCount);
                    for(int i = 0; i < this.outputChannels.size(); i++){
                        if((this.outputChannels.size()-this.pathCount) != i)
                            this.outputChannels.get(i).getDestinyGate().copyVal(2, this.outputChannels.get(i).getInputIndex());
                    }
                    this.outputChannels.get(this.outputChannels.size()-this.pathCount).getDestinyGate().sensitize(this.outputChannels.get(this.outputChannels.size()-this.pathCount).getInputIndex());
                    this.incBack();
                    //this.pathCount--;
                    
                }else{
                    System.out.println(this.name + " "+ this.outputChannels.get(0).getInputIndex());
                    this.outputChannels.get(0).getDestinyGate().sensitize(this.outputChannels.get(0).getInputIndex());
                }
                this.pathCount--;
            }else{
                if(this.bigError == false){
                    System.out.println("terminó con exito");
                    System.out.println("-------------Ruta Verdadera Camios en "+Protoboard.currentSource+" ---------------");
                    for(int i = 0; i < Protoboard.sourcesList.size(); i++){
                        System.out.println(Protoboard.sources.get(Protoboard.sourcesList.get(i)).name + " " + Protoboard.sources.get(Protoboard.sourcesList.get(i)).val);
                    }
                    System.out.println("----------------------------------------------------------------");
                }else{
                    System.out.println("terminó sin exito");
                }
            }
        }else{
            System.out.println("Error al entrar a sensitize " + this.name);
        }
        
    }

    public void incBack(){
        this.pathCount++;
        for(int i = 0; i < this.inputChannels.size(); i++){
            if(this.input[i] == 2){
                if(this.inputChannels.get(i).getType() == 's')
                    this.inputChannels.get(i).getSource();
                if(this.inputChannels.get(i).getType() == 'g')
                    this.inputChannels.get(i).getSourceGate().incBack();
            }
        }
    }

    abstract void justifyFromOutput(int outputVal);
    abstract int getOutput();
    abstract void sensitizeEntry(int index);

    public String toString(){
        String s = "";
        for(int i = 0; i < this.outputChannels.size(); i++)
            s = s + " "+this.outputChannels.get(i).getDestinyGate().getName();
        return s;
    }
}